<?php

/**
 * Framework Dat@ctiv
 *
 * @author		Mathieu Rey
 * @copyright	Copyright (c) 2008, datactiv.fr
 * @link		http://www.datactiv.fr
 */

/**
 * Sql
 *
 * Cette classe offre une interaction simplifiée avec la base de données SQL
 *
 * @author		Mathieu Rey
 */

/**
 *                     TODO
 *
 * - gérer exceptions sur connexion BDD
 * - sortir les regles dans un fichier externe
 */

/*
// ------------------
error_reporting("E^ALL");
set_error_handler('_exception_handler');

function _exception_handler($severity, $message, $filepath, $line){
	echo "<br /><b>".$severity." : </b> ".$message." (fichier : ".$filepath.", ligne : ".$line.")<br /><br />";
}*/

// ------------------

class Sql{


	private $r;			// Pour stocker l'identificateur de retour de la dernière requête

	private $c;			// Lien vers l'objet Crypt


	/**
	 * __construct
	 * 
	 * Etablit la connexion à la base de données
	 */
	 function __construct(){

		 GLOBAL $idents;

		 // Etablit la connexion à la base de données et sélectionne la base
		 mysql_connect($idents["serveur"], $idents["login"], $idents["pass"]);
		 mysql_select_db($idents["base"]);

		 // Crée un objet Crypt, car on en aura probablement besoin
		$this->c = load("Crypt");

	 }

	 function table($t){
		 GLOBAL $props;
		 if (isset($props[$t]))
			 return $props[$t]["nom"];
		 else{
			 erreur("error", "Sql", "Table inexistante");
			 exit;
		 }
	 }

	 function proteger($ch){

		 return mysql_real_escape_string($ch);

	 }

	/**
	 * query
	 * 
	 * Lance une requête SQL
	 */
	 function query($req, $where = "", $lo = ""){

		 // On traite la clause WHERE
		 if (is_array($where)){ // On a un tableau
			 $wh = "";
			 foreach ($where as $k => $v){
				 $wh .= "`".$k."`='".$this->proteger($v)."' AND ";
			 }
			 $wh = " WHERE (".substr($wh, 0, strlen($wh) - 5).")";
			 $req .= $wh." ".$lo;
		 }else{ // On a une chaine (vide possible)
			 $req .= (($where != "") ? " WHERE (".$where.")" : "")." ".$lo;
		 }

		 // DEBUG : afficher la requete
		//echo "<br /><br /><hr /><b>Requete SQL</b> : ".$req."<br />Clause where : ".print_r($where, true)."<br />Autre parametre : ".$lo."<hr /><br /><br />";

		 
		 $this->r = mysql_query($req);

		 if (!$this->r)
			 erreur("error", "Sql", "Requête invalide !");
		 
		 return $this->r;

	 }


	/**
	 * update
	 * 
	 * Pour effectuer un UPDATE dans une table
	 */
	 function update($table, $valeurs, $where = "", $lo = ""){

		 $req = "";
		 $nb = 0;
		 
		 // Prépare la chaine de requête
		 // Formate les données selon le modèle
		 foreach($valeurs as $k => $v){
			 $req .= "`".$k."`='".$this->proteger($this->traiterchaine($k, $v, "in"))."', ";
			 $nb++;
		 }

		 if ($nb == 0){
			 erreur("error", "Sql", "Requete vide !");
			 return;
		 }

		 $req = "UPDATE ".$this->table($table)." SET ".substr($req, 0, strlen($req)-2);

		 // Lance la requete
		 return $this->query($req, $where, $lo);

	 }


	/**
	 * insert
	 * 
	 * Pour effectuer un INSERT dans une table
	 */
	 function insert($table, $valeurs){

		 $cles = "";
		 $vals = "";
		 $nb = 0;
		 
		 // Prépare les deux chaines (une sur les clés, l'autre sur les valeurs
		 foreach($valeurs as $k => $v){
			 $cles .= "`".$k."`, ";
			 // Formate les données selon le modèle
			 $vals .= "'".$this->proteger($this->traiterchaine($k, $v, "in"))."', ";
			 $nb++;
		 }

		 if ($nb == 0){
			 erreur("error", "Sql", "Requete vide !");
			 return;
		 }

		 $req = "INSERT INTO ".$this->table($table)." ( ".substr($cles, 0, strlen($cles)-2)." ) VALUES (".substr($vals, 0, strlen($vals)-2).")";

		 // Lance la requête
		 return $this->query($req);

	 }

	 function delete($table, $where){

		 return $this->query("DELETE FROM ".$this->table($table), $where);

	 }

	 function select($cles, $table, $where = "", $lo = ""){

		 if (is_array($cles))
			 $liste = implode(", ", $cles);
		 else
			 $liste = $cles;

		 return $this->query("SELECT ".$liste." FROM ".$this->table($table), $where, $lo);

	 }


	/**
	 * num_rows
	 * 
	 * Retourne le nombre de résultats de la requête
	 */
	 function num_rows($objet = ""){

		 if ($objet == "")
			 // Pas d'identificateur fourni, on prend celui de la dernière requete
			 if ($this->r == false)
				 return;
			 else
				 return mysql_num_rows($this->r);
		 else
			 // Identificateur fourni, on l'utilise
			 return mysql_num_rows($objet);

	 }


	/**
	 * fetch_object
	 * 
	 * Retourne un objet contenant la réponse courante à la requête
	 */
	 function fetch_object($objet = ""){

		 if ($objet == "")
			 // Pas d'identificateur fourni, on prend celui de la dernière requete
			 if ($this->r == false)
				 return;
			 else
				 $obj = mysql_fetch_object($this->r);
		 else
			 // Identificateur fourni, on l'utilise
			 $obj = mysql_fetch_object($objet);

		if ($obj){
			// Traitement (décryptage, stripslashes...)
			foreach ($obj as $k => &$v)
				$v = $this->traiterchaine($k, $v, "out");
		}

		return $obj;

	 }

	/**
	 * traiterchaine
	 * 
	 * Effectue des transformations sur la chaîne
	 */
	 function traiterchaine($id, $valeur, $sens){

		 GLOBAL $struct;
		 
		$methodes = array(
			"serialize" => array("serialize", "unserialize"),
			"base64" => array("base64_encode", "base64_decode"),
			"scrypt" => array("scrypt", "unscrypt"),
			"crypter" => array("crypter", "uncrypter"),
			"timestamp" => array("donothing", "formaterdate"),
			"slashes" => array("addslashes", "stripslashes")
		);

		$val = $valeur;

		if (!isset($struct[$id]) || !isset($struct[$id]["traitement"]) || $struct[$id]["traitement"] == "" || count($struct[$id]["traitement"]) == 0){
			// Champ non défini, on suppose qu'il n'y a pas besoin de traitement
			return $val;
		}

		$rules = $struct[$id]["traitement"];
		// Si on est en "out" (de la BDD vers l'utilisateur), on parse le tableau à l'envers
		if ($sens == "out")
			$rules = array_reverse($rules);

		// On passe par chaque étape
		foreach ($rules as $k){
			if (isset($methodes[$k])){// La fonction est connue
				if ($k == "scrypt" || $k == "crypter")

					// Appel spécial pour les cryptages (objet)
					$val = $this->c->{$methodes[$k][(($sens == "in") ? 0 : 1)]}($val);

				else
					// Fonction procédurale
					$val = $methodes[$k][(($sens == "in") ? 0 : 1)]($val);
				
			}// On ignore tout paramètre qui ne correspond pas à une fonction répertoriée
		}

		return $val;
// Fonction procédurale
					$val = $methodes[$k][(($sens == "in") ? 0 : 1)]($val);
	 }





}	



/*

Exemple d'utilisation
---------------------

$q = new Sql();

echo "<pre><h1>Insertion</h1>";



$q->insert("test", array("nom" => "L'autre", "prenom" => "Comme disait", "age" => "120"));

// Version SQL brute
$query = $q->select("*", "test", "", "ORDER BY uid ASC");
var_dump(mysql_fetch_object($query));

// Version retraitée
$q->select("*", "test", "", "ORDER BY uid ASC");
var_dump($q->fetch_object());

$q->insert("test", array("nom" => "gfhgf", "prenom" => "Chjhjjhjhrait", "age" => "51"));
$q->insert("test", array("nom" => "jjhjj", "prenom" => "ttttttttt", "age" => "23"));

echo "<br /><h1>Affichage</h1>";

$query = $q->select("*", "test", "", "ORDER BY uid DESC");
echo "Il y a ".$q->num_rows()." enregistrement(s)<br /><br />";

while ($r = $q->fetch_object($query)){
	echo $r->uid." ".$r->nom." ".$r->prenom." ".$r->age."<br />&nbsp;&nbsp;&nbsp;";

	$q->select("nom", "test", array("uid" => $r->uid));
	$s = $q->fetch_object();
	echo $s->nom." ";

	$q->select(array("prenom, age"), "test", array("uid" => $r->uid));
	$s = $q->fetch_object();
	echo $s->prenom." ".$s->age."<br /><br />";
}



$q->query("TRUNCATE ".$q->table("test"));
*/

?>
